﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.ComponentModel;
using System.Reflection;

using System.Linq;
using System.Linq.Expressions;

#if NET_2_0
using RaisingStudio.Linq;
#endif

using RaisingStudio.Data.Providers;
using RaisingStudio.Data.Providers.Adapters;

namespace RaisingStudio.Data.Linq
{
    public class QueryableData<T> : IQueryProvider, IQueryableData<T>, IOrderedQueryable<T>, IQueryable<T>, IEnumerable<T>, IQueryableData, IOrderedQueryable, IQueryable, IEnumerable, IListSource
    {
        #region IEnumerable<T> 成员

        public IEnumerator<T> GetEnumerator()
        {
            return this.ExecuteQuery();
        }

        #endregion

        #region IEnumerable 成员

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion

        #region IQueryable 成员

        public Type ElementType
        {
            get
            {
                return typeof(T);
            }
        }

        private System.Linq.Expressions.Expression expression;
        public System.Linq.Expressions.Expression Expression
        {
            get
            {
                if (this.expression != null)
                {
                    return this.expression;
                }
                else
                {
                    return System.Linq.Expressions.Expression.Constant(this);
                }
            }
        }

        public IQueryProvider Provider
        {
            get
            {
                return this;
            }
        }

        #endregion

        #region IQueryProvider 成员

        IQueryable<S> IQueryProvider.CreateQuery<S>(System.Linq.Expressions.Expression expression)
        {
            return new QueryableData<S>(this.DataContext, expression);
        }

        public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
        {
            return new QueryableData<T>(this.DataContext, expression);
        }

        public S Execute<S>(System.Linq.Expressions.Expression expression)
        {
            // TODO:
            object value = Execute(expression);
#if (PocketPC || Smartphone || WindowsCE)
                            value = Convert.ChangeType(value, typeof(S), null);
#else
            value = Convert.ChangeType(value, typeof(S));
#endif
            return (S)value;
        }

        public object Execute(System.Linq.Expressions.Expression expression)
        {
            Type type = GetDataType(expression);
            RaisingStudio.Data.Common.Builders.CommonCommandBuilder commonCommandBuilder = (dataContext.Provider as DataProvider).GetDataTableAdapter(type).CommandBuilder.CommonCommandBuilder;
            string[] columns;
#if !(PocketPC || Smartphone || WindowsCE)
            CommonCommand command = null;
            if (dataContext.DatabaseType == DatabaseType.SHAREPOINT)
            {
                command = SharePointExpressionCommandBuilder.GetCommand(commonCommandBuilder, expression, out columns);
            }
            else
            {
                command = ExpressionCommandBuilder.GetCommand(commonCommandBuilder, expression, out columns);
            }
#else
            CommonCommand command = ExpressionCommandBuilder.GetCommand(commonCommandBuilder, expression, out columns);
#endif
            if (type == typeof(T))
            {
                return this.dataContext.ExecuteScalar<T>(command);
            }
            else
            {
                return (this.dataContext.Provider as DataProvider).GetDataTableAdapter(type).ExecuteScalarCommand(command);                
            }
        }

        #endregion

        #region IListSource 成员

        public bool ContainsListCollection
        {
            get
            {
                return false;
            }
        }

        private List<T> list;

        public IList GetList()
        {
            if (list == null)
            {
                list = new List<T>();
                foreach (T t in this)
                {
                    list.Add(t);
                }
            }
            return list;
        }

        #endregion

        private DataContext dataContext;
        public DataContext DataContext
        {
            get { return dataContext; }
            set { dataContext = value; }
        }

        public QueryableData(DataContext dataContext)
        {
            this.dataContext = dataContext;
        }

        public QueryableData(DataContext dataContext, System.Linq.Expressions.Expression expression)
        {
            this.dataContext = dataContext;
            this.expression = expression;
        }

        private IEnumerator<T> ExecuteQuery()
        {
            Type type = GetDataType(this.expression);
            if (type == typeof(T))
            {
                RaisingStudio.Data.Common.Builders.CommonCommandBuilder commonCommandBuilder = (dataContext.Provider as DataProvider).GetDataTableAdapter(type).CommandBuilder.CommonCommandBuilder;
                string[] columns;
#if !(PocketPC || Smartphone || WindowsCE)
                CommonCommand command = null;
                if (dataContext.DatabaseType == DatabaseType.SHAREPOINT)
                {
                    command = SharePointExpressionCommandBuilder.GetCommand(commonCommandBuilder, this.expression, out columns);
                }
                else
                {
                    command = ExpressionCommandBuilder.GetCommand(commonCommandBuilder, this.expression, out columns);
                }
#else
                CommonCommand command = ExpressionCommandBuilder.GetCommand(commonCommandBuilder, this.expression, out columns);
#endif
                if ((columns != null) && (columns.Length > 0))
                {
                    IEnumerator enumerator = null;
                    enumerator = ((this.dataContext.Provider as RaisingStudio.Data.Providers.DataProvider).GetDataObjectAdapter<T>().GetEnumerator(columns, command)).GetEnumerator();
                    return (IEnumerator<T>)enumerator;
                }
                else
                {
                    IEnumerator enumerator = null;
                    enumerator = ((this.dataContext.Provider as RaisingStudio.Data.Providers.DataProvider).GetDataObjectAdapter<T>().GetEnumerator(command)).GetEnumerator();
                    return (IEnumerator<T>)enumerator;
                }
            }
            else
            {
                RaisingStudio.Data.Common.Builders.CommonCommandBuilder commonCommandBuilder = (dataContext.Provider as DataProvider).GetDataTableAdapter(type).CommandBuilder.CommonCommandBuilder;
                string[] columns;
#if !(PocketPC || Smartphone || WindowsCE)
                CommonCommand command = null;
                if (dataContext.DatabaseType == DatabaseType.SHAREPOINT)
                {
                    command = SharePointExpressionCommandBuilder.GetCommand(commonCommandBuilder, this.expression, out columns);
                }
                else
                {
                    command = ExpressionCommandBuilder.GetCommand(commonCommandBuilder, this.expression, out columns);
                }
#else
                CommonCommand command = ExpressionCommandBuilder.GetCommand(commonCommandBuilder, this.expression, out columns);
#endif
                if ((columns != null) && (columns.Length > 0))
                {
                    Type genericDataObjectAdapterType = typeof(RaisingStudio.Data.Providers.Adapters.Generic.DataObjectAdapter<>).MakeGenericType(type);
                    object genericDataObjectAdapter = Activator.CreateInstance(genericDataObjectAdapterType);
                    MethodInfo methodInfo = genericDataObjectAdapterType.GetMethod("GetEnumerator", new Type[] { typeof(string[]), typeof(RaisingStudio.Data.CommonCommand) });
                    IEnumerable enumerable = (IEnumerable)(methodInfo.Invoke(genericDataObjectAdapter, new object[] { columns, command }));
                    System.Linq.Expressions.UnaryExpression selectExpression = (this.expression as MethodCallExpression).Arguments[1] as System.Linq.Expressions.UnaryExpression;
                    System.Linq.Expressions.MethodCallExpression m = System.Linq.Expressions.MethodCallExpression.Call(null, (this.expression as MethodCallExpression).Method, enumerable.AsQueryable().Expression, selectExpression);
                    IQueryable<T> q = enumerable.AsQueryable().Provider.CreateQuery<T>(m);
                    return q.GetEnumerator();
                }
                else
                {
                    Type genericDataObjectAdapterType = typeof(RaisingStudio.Data.Providers.Adapters.Generic.DataObjectAdapter<>).MakeGenericType(type);
                    object genericDataObjectAdapter = Activator.CreateInstance(genericDataObjectAdapterType);
                    MethodInfo methodInfo = genericDataObjectAdapterType.GetMethod("GetEnumerator", new Type[] { typeof(RaisingStudio.Data.CommonCommand) });
                    IEnumerable enumerable = (IEnumerable)(methodInfo.Invoke(genericDataObjectAdapter, new object[] { command }));
                    System.Linq.Expressions.UnaryExpression selectExpression = (this.expression as MethodCallExpression).Arguments[1] as System.Linq.Expressions.UnaryExpression;
                    System.Linq.Expressions.MethodCallExpression m = System.Linq.Expressions.MethodCallExpression.Call(null, (this.expression as MethodCallExpression).Method, enumerable.AsQueryable().Expression, selectExpression);
                    IQueryable<T> q = enumerable.AsQueryable().Provider.CreateQuery<T>(m);
                    return q.GetEnumerator();
                }
            }
        }


        private Type GetDataType(System.Linq.Expressions.Expression expression)
        {
            if (expression is MethodCallExpression)
            {
                MethodCallExpression m = expression as MethodCallExpression;
                if (m.Method.Name == "Select")
                {
                    return GetDataType(m.Arguments[0]);
                }
                else if (m.Method.Name == "Count")
                {
                    return GetDataType(m.Arguments[0]);
                }
                else if (m.Method.Name == "Average") 
                {
                    return GetDataType(m.Arguments[0]);
                }
                else if((m.Method.Name == "Max") || (m.Method.Name == "Min") || (m.Method.Name == "Sum"))
                {
                    return GetDataType(m.Arguments[0]);
                }
            }
            if (expression.Type.IsGenericType)
            {
                return expression.Type.GetGenericArguments()[0];
            }
            else
            {
                return expression.Type;
            }
        }
    }
}
